package br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.Arc;
import br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.Node;
import br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.PetriNet;
import br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.Place;
import br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.ToolSpecific;
import br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.Transition;

public class PNMLParser {

	private String getType(Element model) {
		Element type = (Element) model.getElementsByTagName("type").item(0);
		try {
			Element t = (Element) type.getElementsByTagName("text").item(0);
			return t.getChildNodes().item(0).getTextContent().trim();
		} catch (NullPointerException e) {
			return "";
		}
	}

	private String getName(Element model) {
		Element name = (Element) model.getElementsByTagName("name").item(0);
		try {
			Element t = (Element) name.getElementsByTagName("text").item(0);
			return t.getChildNodes().item(0).getTextContent().trim();
		} catch (NullPointerException e) {
			return "";
		}
	}

	private String getInitialMarking(Element model) {
		Element im = (Element) model.getElementsByTagName("initialMarking")
				.item(0);
		try {
			Element t = (Element) im.getElementsByTagName("text").item(0);
			return t.getChildNodes().item(0).getTextContent().trim();
		} catch (NullPointerException e) {
			return "";
		}
	}

	private String getInscription(Element model) {
		Element in = (Element) model.getElementsByTagName("inscription")
				.item(0);
		try {
			Element t = (Element) in.getElementsByTagName("text").item(0);
			return t.getChildNodes().item(0).getTextContent().trim();
		} catch (NullPointerException e) {
			return "";
		}
	}

	private ToolSpecific getToolSpecific(Element model) {
		Element ts = (Element) model.getElementsByTagName("toolspecific").item(
				0);
		String tool = ts.getAttribute("tool");
		String version = ts.getAttribute("version");
		Element fm = (Element) ts.getElementsByTagName("finalmarking").item(0);
		String xmlns = fm.getAttribute("xmlns");
		Element t = (Element) fm.getElementsByTagName("text").item(0);
		ToolSpecific toolSpecific = new ToolSpecific();
		toolSpecific.setFinalMarking(t.getTextContent());
		toolSpecific.setTool(tool);
		toolSpecific.setVersion(version);
		toolSpecific.setXmlns(xmlns);
		return toolSpecific;
	}

	private List<Arc> getArcs(List<br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.Element> objs) {
		List<Arc> arcs = new ArrayList<Arc>();
		for (br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.Element obj : objs) {
			if (obj instanceof Arc)
				arcs.add((Arc) obj);
		}
		return arcs;
	}
	
	private List<Arc> getIncomingArcs(List<Arc> arcs, br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.Element node) {
		List<Arc> inc = new ArrayList<Arc>();
		for (Arc arc : arcs) {
			if (arc.getTarget().equals(node))
				inc.add(arc);
		}
		return inc;
	}
	
	private List<Arc> getOutgoingArcs(List<Arc> arcs, br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.Element node) {
		List<Arc> inc = new ArrayList<Arc>();
		for (Arc arc : arcs) {
			if (arc.getSource().equals(node))
				inc.add(arc);
		}
		return inc;
	}
	
	private List<Place> getPlaces(List<Arc> arcs, Element model) {
		List<Place> places = new ArrayList<Place>();
		NodeList placeNodes = model.getElementsByTagName("place");
		for (int i = 0; i < placeNodes.getLength(); i++) {
			Element placeElm = (Element) placeNodes.item(i);
			Place p = new Place();
			p.setId(placeElm.getAttribute("id"));
			p.setName(getName(placeElm));
			p.setType(getType(placeElm));
			p.setInitialMarking(getInitialMarking(placeElm));
			places.add(p);
		}
		return places;
	}

	private List<Transition> getTransitions(Element model) {
		List<Transition> transitions = new ArrayList<Transition>();
		NodeList transitionNodes = model.getElementsByTagName("transition");
		for (int i = 0; i < transitionNodes.getLength(); i++) {
			Element transitionElm = (Element) transitionNodes.item(i);
			Transition t = new Transition();
			t.setId(transitionElm.getAttribute("id"));
			t.setName(getName(transitionElm));
			transitions.add(t);
		}
		return transitions;
	}

	private Node getNode(List<br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.Element> nodes, String id) {
		for (br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.Element node : nodes) {
			if (node instanceof Transition)
				if (((Transition) node).getId().equals(id))
					return (Node) node;
			if (node instanceof Place)
				if (((Place) node).getId().equals(id))
					return (Node) node;
		}
		return null;
	}

	private List<Arc> getArcs(List<br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.Element> objects, Element model) {
		List<Arc> arcs = new ArrayList<Arc>();
		NodeList arcNodes = model.getElementsByTagName("arc");
		for (int i = 0; i < arcNodes.getLength(); i++) {
			Element arcElm = (Element) arcNodes.item(i);
			Arc a = new Arc();
			a.setId(arcElm.getAttribute("id"));
			a.setSource(getNode(objects, arcElm.getAttribute("source")));
			a.setTarget(getNode(objects, arcElm.getAttribute("target")));
			a.setInscription(getInscription(arcElm));
			arcs.add(a);
		}
		return arcs;
	}
	
	private void resolveArcs(List<br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.Element> objs) {
		for (br.edu.ufcg.gmf.mdaveritas.util.pnmlconverter.entities.Element node : objs) {
			if (node instanceof Transition) {
				((Transition) node).setIncArcs(getIncomingArcs(getArcs(objs), node));
				((Transition) node).setOutArcs(getOutgoingArcs(getArcs(objs), node));
			}
			if (node instanceof Place) {
				((Place) node).setIncArcs(getIncomingArcs(getArcs(objs), node));
				((Place) node).setOutArcs(getOutgoingArcs(getArcs(objs), node));
			}
		}
	}

	private List<PetriNet> getNets(Element model) {
		List<PetriNet> nets = new ArrayList<PetriNet>();
		NodeList netNodes = model.getElementsByTagName("net");
		for (int i = 0; i < netNodes.getLength(); i++) {
			Element netElm = (Element) netNodes.item(i);
			PetriNet pn = new PetriNet();
			pn.setId(netElm.getAttribute("id"));
			pn.setType(netElm.getAttribute("type"));
			pn.getObjects().addAll(getPlaces(getArcs(pn.getObjects()),netElm));
			pn.getObjects().addAll(getTransitions(netElm));
			pn.getObjects().addAll(getArcs(pn.getObjects(), netElm));
			resolveArcs(pn.getObjects());
			pn.setToolSpecific(getToolSpecific(netElm));
			nets.add(pn);
		}
		return nets;
	}

	public List<PetriNet> getRoot(String filepath) {
		File file = new File(filepath);
		List<PetriNet> nets = new ArrayList<PetriNet>();
		try {
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db;
			db = dbf.newDocumentBuilder();
			Document doc = db.parse(file);
			doc.getDocumentElement().normalize();

			NodeList nodeLst = doc.getElementsByTagName("pnml");
			Element model;
			for (int i = 0; i < nodeLst.getLength(); i++) {
				model = (Element) nodeLst.item(i);
				nets = getNets(model);
			}

		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return nets;
	}

	public static void main(String[] args) {
		PNMLParser x = new PNMLParser();
		List<PetriNet> pn = x.getRoot("auctionService.pnml");
		PNMLGenerator pnmlg = new PNMLGenerator(pn);
		pnmlg.createXML("C:\\Temp\\out.xml");
	}
}